package org.openzip.openzip.gui.util;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.swing.Icon;
import javax.swing.filechooser.FileSystemView;

/**
 * This is an utility class that provides various methods for the
 * <code>File</code> class and file tree manipulation.
 * 
 * @author Jakub Závěrka
 */
public abstract class FileUtil
{
	//==== FILE LOADER ==========
	private static Collection<File> utilCol = new ArrayList<File>();

	/**
	 * Returns all subfiles in the given folder and recurently all files and
	 * folders in all subfolders of the given folder.
	 * 
	 * @param f
	 *           Specified folder to load subfiles for.
	 * @param includeDirs
	 *           If the resulting collection should contain directories as well
	 *           or subfiles only.
	 * @return Colelction of all subfiles of the given folder.
	 */
	public static Collection<File> getLeafs(File f, boolean includeDirs){
		//clear the utility collection
		utilCol.clear();
		//if the file is not a folder
		if(f.isFile()){
			//add it to the colection and return it
			utilCol.add(f);
			return new ArrayList<File>(utilCol);
		}
		//else recurently load all the subfiles in all subdirectories 
		load(f, includeDirs);
		//return new collection of loaded files
		return new ArrayList<File>(utilCol);
	}
	
	/**
	 * This loads all subfiles for the given folder to the collection. It gets
	 * recursivelly called for each subfolder.
	 * 
	 * @param f
	 *           Folder to be loaded.
	 * @param includeDirs
	 *           If folders should be added to the collection as well.
	 */
	private static void load(File f, boolean includeDirs){
		//subfiles of the folder
		File[] files = f.listFiles();
		//if file is not a folder - there is nothing else we can do
		if(files == null){
			return;
		}
		//if dirs should be included, add the specified file
		if(includeDirs){
			utilCol.add(f);
		}
		//for each subfile, add it to the collection if it's a file or load it's 
		//subfiles if it's a folder
		for(int i = 0; i<files.length; i++){
			if(files[i].isDirectory()){
				load(files[i], includeDirs);
			}
			else{
				utilCol.add(files[i]);
			}
		}
	}
	
	/**
	 * Splits the filepath of the given file by the current system file
	 * separator.
	 * 
	 * @param f
	 *           The file to split its filepath.
	 * @return An array of <code>Strig</code>s representing each folder on the
	 *         given file's path.
	 */
	public static String[] splitFilePath(File f){
		String separator = 
			System.getProperty("file.separator").replaceAll("\\\\", "\\\\\\\\");
		return f.getAbsolutePath().split(separator);
	}

	//=========== FILE ICON FACTORY ===========
	/**
	 * Returns an <code>Icon</code> representing the given file, as it would look
	 * like on the given platform. The returned icon is the small one (16x16
	 * pixels). The method utilizes caching based on the file extension (caches
	 * icons for the given extension, so the icon for a given exetnsion is loaded
	 * only once). The exception are folders, theirs icons are loaded without
	 * caching (as every folder may have a different icon).
	 */
	public static Icon getFileIcon(File f){
		Icon icon = null;
		try{
			//if the folder is dir
			if(f.isDirectory()){
				try{
					//try to fetch its icon
					icon = FileSystemView.getFileSystemView().getSystemIcon(f);
				}
				//if an exception occurs
				catch(Exception e){
					// create a temp folder
					File file = File.createTempFile("icon", "dir");
					file.delete();
					file.mkdir();
					//and fetch its icon
					icon = FileSystemView.getFileSystemView().getSystemIcon(file);
					// delete temp folder
					file.delete();
				}
			}
			//file is not a dir
			else{
				//icon will be loaded based on the extension
				icon = getIconByExtension(getFileExtension(f));
			}
		}
		catch(IOException e){
			e.printStackTrace();
		}
		return icon;
	}

	//cache for the icons and extensions
	private static Map<String, Icon> iconCache = new HashMap<String, Icon>();
	
	/**
	 * This will return an icon for the given file extension as it would look
	 * like on the given platform. Loaded icons are cached, so it won't load icon
	 * twice for the same extension.
	 * 
	 * @param extension
	 *           File extension to load icon for.
	 * @return Icon for the extension.
	 * @throws IOException
	 *            If an input/output error occurs.
	 */
	private static Icon getIconByExtension(String extension) throws IOException{
		Icon icon;
		//try to load from cache
		icon = iconCache.get(extension);
		//if there is no icon in the cache for the extension 
		if(icon == null){
			// Create a temporary file with the specified extension
			File file = File.createTempFile("icon", "." + extension);
			//fetch the icon for it
			icon = FileSystemView.getFileSystemView().getSystemIcon(file);
			//delete temp file
			file.delete();
			//store in cache
			iconCache.put(extension, icon);
		}
		return icon;
	}
	
	/**
	 * Returns the extension of the specified file.
	 * @param file Specified file to get extension for.
	 * @return Extension of the specified file.
	 */
	private static String getFileExtension(File file) {
		int i = file.getName().lastIndexOf(".");
		if (i <= 0) {
			return "";
		}
		// i+1 removes the dot from extension
		String ext = file.getName().substring(i + 1).toLowerCase();
		if (ext != null) {
			return ext;
		}
		return "";
	}
}
